﻿using System;
using System.Collections.Generic;
using SymbolIntegration.core.Helpers;

namespace SymbolIntegration.core
{
    public abstract class Expression
    {
        static public Expression operator +(Expression exprLeft, Expression exprRight)
        {
            return ExpressionOperatorReloader.Plus(exprLeft, exprRight);
        }

        static public Expression operator -(Expression exprLeft, Expression exprRight)
        {
            return ExpressionOperatorReloader.Minus(exprLeft, exprRight);
        }

        static public Expression operator *(Expression exprLeft, Expression exprRight)
        {
            return ExpressionOperatorReloader.Multiple(exprLeft, exprRight);
        }

        static public Expression operator /(Expression exprLeft, Expression exprRight)
        {
            return ExpressionOperatorReloader.Divide(exprLeft, exprRight);
        }

        static public Expression operator ^(Expression exprLeft, Expression exprRight)
        {
            return ExpressionOperatorReloader.Power(exprLeft, exprRight);
        }

        static public Expression Integrate(Expression expression, string variable, IIntegrator integrator)
        {
            var result = expression.Normalize().Integrate(variable, integrator);
            return result;
        }

        protected static bool IsListsEqual(List<Expression> leftList, List<Expression> rightList)
        {
            var equalPositionsSet = new HashSet<int>();
            bool isEqual = leftList.Count == rightList.Count;
            do
            {
                if (!isEqual)
                {
                    break;
                }

                foreach (var expression in leftList)
                {
                    for (int i = 0; i < rightList.Count; i++)
                    {
                        if (equalPositionsSet.Contains(i))
                        {
                            continue;
                        }

                        if (expression.IsEqual(rightList[i]))
                        {
                            equalPositionsSet.Add(i);
                            break;
                        }
                    }
                }
                isEqual = equalPositionsSet.Count == leftList.Count;
            }
            while (false);

            return isEqual;
        }

        public static Expression NormalizeByVar(Expression expr, string var)
        {
            var result = expr.Normalize();
            result = result.NormalizeByVariable(var);
            return result;
        }

        protected Expression()
        {
            IsNegate = false;
        }

        protected internal readonly ExpressionDependencyCache m_Cache = new ExpressionDependencyCache();

        public bool IsNegate { get; private set; }

        public Expression SetNegate(bool isNegate)
        {
            var expr = Clone();
            expr.IsNegate = isNegate;
            return expr;
        }
        /// <summary>
        /// Like opening scopes with minus or plus before. 
        /// </summary>
        /// <param name="isNegate">
        /// Tells is minus before expression
        /// </param>
        /// <returns></returns>
        public Expression InvertNegate(bool isNegate)
        {
            var expr = Clone();
            expr.IsNegate ^= isNegate;
            return expr;
        }

        public bool IsDependsFrom(string variable)
        {
            if (!m_Cache.IsValid)
            {
                RebuildDependancyCache();
            }
            return m_Cache.Varibles.Contains(variable);
        }

        public ExpressionStatistic GetStatistic(string integrateVariable)
        {
            var statistic = new ExpressionStatistic();
            RebuildDependancyCache();
            statistic.Variables = new List<string>(m_Cache.Varibles);
            FillExpressionStatistic(statistic, integrateVariable);
            return statistic;
        }
        
        public Polinom FormatToPolinom(string variable)
        {
            Polinom polinom;
            if (!IsDependsFrom(variable))
            {
                polinom = new Polinom(variable);
                polinom.SetConstantAt(0, Clone());
            }
            else
            {
                polinom = FormatToPolinomAsDependentFromVariable(variable);
            }
            return polinom;
        }

        protected virtual Expression NormalizeByVariable(string var)
        {
            return this;
        }

        public abstract Expression Clone();
        public abstract ExpressionType Type { get;}
        public abstract Expression Substitute(Dictionary<string, Expression> varValues);
        public abstract Expression Differentiate(string variable);
        public abstract Expression Normalize();
        public abstract bool IsEqual(Expression right);
        public abstract Polinom FormatToPolinomAsDependentFromVariable(string variable);

        protected internal abstract void RebuildDependancyCache();
        protected internal abstract void FillExpressionStatistic(ExpressionStatistic statistic, string integrateVariable);
        //Always normalized
        protected internal abstract Expression IntegrateAsDependentFromVar(string variable, IIntegrator integrator);

        protected internal void InnerCopy(Expression expr)
        {
            expr.IsNegate = IsNegate;
        }

        protected internal Expression Integrate(string variable, IIntegrator integrator)
        {
            Expression result = !IsDependsFrom(variable) ? IntegrationHelper.IntegrateAsConstant(this, variable) : 
                IntegrateAsDependentFromVar(variable, integrator);
            return result;
        }

        public override string ToString()
        {
            throw new NotImplementedException("ToString must be reloaded");
        }
    }
}
